package br.edu.unifei.mestrado.commons.graph;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Scanner;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class GraphWrapper {


	private Logger logger = LoggerFactory.getLogger(this.getClass());

	protected int sizeNodes = 0;
	private Integer level;
	private long idEdge = 0;
	protected String graphFileName;

	public static final Integer NO_LEVEL = null;
	public static final Integer FIRST_lEVEL = 1;

	public GraphWrapper(Integer level, String graphFileName) {
		this.level = level;
		this.graphFileName = graphFileName;
	}

	public abstract void beginTransaction();

	public abstract void endTransaction();

	public abstract void finish();

	public abstract void shutdown();

	public abstract NodeWrapper getNode(long id);

	public abstract NodeWrapper createNode(long id, int weight);

	public abstract Iterable<NodeWrapper> getAllNodes();

	protected abstract RelationshipWrapper createRelationship(long id, int newWeight, long startNode,
			long endNode);

	public abstract Iterable<RelationshipWrapper> getAllEdges();

	public abstract RelationshipWrapper getArestaLinking(NodeWrapper va, NodeWrapper vb);

	protected abstract NodeWrapper getOrCreateNode(Long id, int weight);

	public void readGraph() {
		InputStream in = System.in;
		if (graphFileName != null) {
			try {
				in = new FileInputStream(graphFileName);
			} catch (FileNotFoundException e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			}
		}

		if (in == System.in) {
			System.out.println("Digite os valores do grafo: ");
		} else {
			logger.debug("Lendo arquivo: " + graphFileName);
		}
		Scanner sc = new Scanner(in);
		int size = sc.nextInt();

		for (int n = 0; n < size; n++) {
			String line = sc.next() + sc.nextLine();
			String[] splited = line.split("\\ ");
			if (splited.length > 0) {
				String nodeId1 = splited[0];
				Long nodeA = Long.parseLong(nodeId1);

				for (int i = 1; i < splited.length; i++) {
					String nodeId2 = splited[i];
					Long nodeB = Long.parseLong(nodeId2);

					// UTIL: se existir uma aresta para o proprio vertice, ela eh descartada.
					if (nodeA == nodeB) {
						continue;
					}
					createChain(idEdge, 1, nodeA, 1, nodeB, 1);
					idEdge++;
				}
			}
		}
	}

	public void createChain(long idEdge, int relWeight, Long id1, int weight1, Long id2, int weight2) {
		NodeWrapper firstNode = getOrCreateNode(id1, weight1);
		NodeWrapper secondNode = getOrCreateNode(id2, weight2);
		createRelationship(idEdge, relWeight, firstNode.getId(), secondNode.getId());
	}

	public int getSizeNodes() {
		return sizeNodes;
	}

	public Integer getLevel() {
		return level;
	}

	public void unlockAllNodes() {
		Iterable<NodeWrapper> all = getAllNodes();
		for (NodeWrapper node : all) {
			if (node.hasProperty(GraphProperties.ID)) {
				node.unlock();
			} else {
				// TODO_: verificar pq o node zero nao tem name
				// UTIL: porque o referenceNode é controlado pelo banco
				// logger.debug("Node " + node.getId() + " has no property " + KLDBProperties.ID);
			}
		}
	}

	public void resetPartitionAllNodes() {
		Iterable<NodeWrapper> all = getAllNodes();
		for (NodeWrapper node : all) {
			if (node.hasProperty(GraphProperties.ID)) {
				node.resetPartition();
			}
		}
	}

	public void printGraph() {
		logger.debug("Informacoes do grafo: \n{} \n{}", new Object() {
			public String toString() {
				StringBuffer b = new StringBuffer();
				Iterable<RelationshipWrapper> relationships = getAllEdges();
				b.append("Arestas: ");
				int count = 0;
				for (RelationshipWrapper aresta : relationships) {
					b.append(aresta.getId()).append(": ").append(aresta.getStartNode().getId()).append("-")
							.append(aresta.getEndNode().getId()).append(", ");
					count++;
					if (count > 100) {
						b.append("Arestas interrompidas maior que 100");
						break;
					}
				}
				return b.toString();
			}
		}, new Object() {
			public String toString() {
				StringBuffer b = new StringBuffer();
				Iterable<NodeWrapper> nodes = getAllNodes();
				b.append("Vertices: ");
				int count = 0;
				for (NodeWrapper node : nodes) {
					b.append(node.getId()).append(", ");
					count++;
					if (count > 100) {
						b.append("Nós interrompidos maior que 100");
						break;
					}
				}
				return b.toString();
			}
		});
	}
	
	public String getGraphFileName() {
		return graphFileName;
	}
}
